package app.server.project.core.tcp;

import app.server.project.core.constants.TcpCmd;
import app.server.project.core.exception.GlobalException;
import app.server.project.core.msgdispatcher.Dispatcher;
import app.server.project.core.tcp.model.TcpReqModel;
import app.server.project.core.tcp.model.TcpRespModel;
import cn.hutool.json.JSONUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 消息分发器
 */
@Slf4j
public class SessionHandler extends ChannelInboundHandlerAdapter {


    private TcpSessionMgr tcpSessionMgr;
    private Dispatcher dispatcher;


    public SessionHandler(TcpSessionMgr tcpSessionMgr, Dispatcher dispatcher) {
        this.tcpSessionMgr = tcpSessionMgr;
        this.dispatcher = dispatcher;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object frame) throws Exception {

        TcpReqModel tcpReqModel = (TcpReqModel) frame;
        switch (tcpReqModel.getCmd()) {
            case TcpCmd.Heart: {
                ctx.writeAndFlush(tcpReqModel);
            }
            break;
            case TcpCmd.Login: {
                log.info("收到tcp login 上报参数:{}",tcpReqModel);
                this.tcpSessionMgr.addSession(ctx, tcpReqModel);
            }
            break;
            default:{

                try {
                    var method=this.dispatcher.getMethodByCmd(tcpReqModel.getCmd());
                    if (Objects.isNull(method)){
                        log.error("命令字无法找到处理器,请求原始数据:{}", tcpReqModel);
                        return;
                    }
                    var id=this.tcpSessionMgr.getIdBySession(ctx);
                    if (Objects.isNull(id)){
                        log.error("无登录,请求原始数据:{}", tcpReqModel);
                        ctx.close();
                        return;
                    }
                    log.info("id:{}收到请求:{}",id,tcpReqModel);
                    Object invokeRet=null;
                    if (JSONUtil.isTypeJSONObject(tcpReqModel.getData())){
                        invokeRet=method.getMethodInfo().getMethod().invoke(method.getMethodInfo().getObj(),id,JSONUtil.toBean(tcpReqModel.getData(),method.getType()));
                    }else {
                        if (Objects.isNull(tcpReqModel.getData())){
                            invokeRet=method.getMethodInfo().getMethod().invoke(method.getMethodInfo().getObj(),id);
                        }else {
                            invokeRet=method.getMethodInfo().getMethod().invoke(method.getMethodInfo().getObj(),id,tcpReqModel.getData());
                        }
                    }
                    if (Objects.nonNull(invokeRet)){
                        log.info("id:{}:cmd:{}应答:{}",id,tcpReqModel.getCmd(),invokeRet);
                        ctx.writeAndFlush(TcpRespModel.of(tcpReqModel.getCmd(),invokeRet));
                    }
                }catch (Exception e){
                    if (e instanceof GlobalException){
                        var code=((GlobalException)e);
                        var id=this.tcpSessionMgr.getIdBySession(ctx);
                        if (Objects.nonNull(id)){
                            log.info("id:{}应答:{}",id,code.getCode());
                        }

                        ctx.write(TcpRespModel.of(TcpCmd.Error,code.getCode()));
                    }
                    log.error("tcp消息派发器执行报错",e);
                }
            }
            break;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        this.tcpSessionMgr.doExceptionClose(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            switch (idleStateEvent.state()) {
                case READER_IDLE: {
                    this.tcpSessionMgr.doExceptionClose(ctx);
                }
                break;
                case WRITER_IDLE: {
                    //ctx.flush();
                }
                break;
            }
        }
    }
}
